/*
 * Copyright (C) 2005 - 2013 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008 - 2013 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010 - 2013 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef ARKCORE_MAP_H
#define ARKCORE_MAP_H

#include "Define.h"
#include <ace/RW_Thread_Mutex.h>
#include <ace/Thread_Mutex.h>

#include "DBCStructure.h"
#include "GridDefines.h"
#include "Cell.h"
#include "Timer.h"
#include "SharedDefines.h"
#include "GridRefManager.h"
#include "MapRefManager.h"

#include <bitset>
#include <list>

class Unit;
class WorldPacket;
class InstanceScript;
class Group;
class InstanceSave;
class Object;
class WorldObject;
class TempSummon;
class Player;
class CreatureGroup;
struct ScriptInfo;
struct ScriptAction;
struct Position;
class Battleground;
class MapInstanced;
class InstanceMap;
namespace Trinity
{
    struct ObjectUpdater;
}

struct ScriptAction
{
    uint64 sourceGUID;
    uint64 targetGUID;
    uint64 ownerGUID;          // owner of source if source is item
    ScriptInfo const* script;          // pointer to static script data
};

// ******************************************
// Map file format defines
// ******************************************
#define MAP_MAGIC             'SPAM'
#define MAP_VERSION_MAGIC     '1.1v'
#define MAP_AREA_MAGIC        'AERA'
#define MAP_HEIGHT_MAGIC      'TGHM'
#define MAP_LIQUID_MAGIC      'QILM'

struct map_fileheader
{
    uint32 mapMagic;
    uint32 versionMagic;
    uint32 buildMagic;
    uint32 areaMapOffset;
    uint32 areaMapSize;
    uint32 heightMapOffset;
    uint32 heightMapSize;
    uint32 liquidMapOffset;
    uint32 liquidMapSize;
};

#define MAP_AREA_NO_AREA      0x0001

struct map_areaHeader
{
    uint32 fourcc;
    uint16 flags;
    uint16 gridArea;
};

#define MAP_HEIGHT_NO_HEIGHT  0x0001
#define MAP_HEIGHT_AS_INT16   0x0002
#define MAP_HEIGHT_AS_INT8    0x0004

struct map_heightHeader
{
    uint32 fourcc;
    uint32 flags;
    float gridHeight;
    float gridMaxHeight;
};

#define MAP_LIQUID_NO_TYPE    0x0001
#define MAP_LIQUID_NO_HEIGHT  0x0002

struct map_liquidHeader
{
    uint32 fourcc;
    uint16 flags;
    uint16 liquidType;
    uint8 offsetX;
    uint8 offsetY;
    uint8 width;
    uint8 height;
    float liquidLevel;
};

enum ZLiquidStatus
{
    LIQUID_MAP_NO_WATER = 0x00000000, LIQUID_MAP_ABOVE_WATER = 0x00000001, LIQUID_MAP_WATER_WALK = 0x00000002, LIQUID_MAP_IN_WATER = 0x00000004, LIQUID_MAP_UNDER_WATER = 0x00000008
};

#define MAP_LIQUID_TYPE_NO_WATER    0x00
#define MAP_LIQUID_TYPE_WATER       0x01
#define MAP_LIQUID_TYPE_OCEAN       0x02
#define MAP_LIQUID_TYPE_MAGMA       0x04
#define MAP_LIQUID_TYPE_SLIME       0x08

#define MAP_ALL_LIQUIDS   (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME)

#define MAP_LIQUID_TYPE_DARK_WATER  0x10
#define MAP_LIQUID_TYPE_WMO_WATER   0x20

struct LiquidData
{
    uint32 type;
    float level;
    float depth_level;
};

class GridMap
{
    uint32 m_flags;
    // Area data
    uint16 m_gridArea;
    uint16 *m_area_map;
    // Height level data
    float m_gridHeight;
    float m_gridIntHeightMultiplier;
    union
    {
        float *m_V9;
        uint16 *m_uint16_V9;
        uint8 *m_uint8_V9;
    };
    union
    {
        float *m_V8;
        uint16 *m_uint16_V8;
        uint8 *m_uint8_V8;
    };
    // Liquid data
    uint16 m_liquidType;
    uint8 m_liquid_offX;
    uint8 m_liquid_offY;
    uint8 m_liquid_width;
    uint8 m_liquid_height;
    float m_liquidLevel;
    uint8 *m_liquid_type;
    float *m_liquid_map;

    bool loadAreaData (FILE *in, uint32 offset, uint32 size);
    bool loadHeihgtData (FILE *in, uint32 offset, uint32 size);
    bool loadLiquidData (FILE *in, uint32 offset, uint32 size);

    // Get height functions and pointers
    typedef float (GridMap::*pGetHeightPtr) (float x, float y) const;
    pGetHeightPtr m_gridGetHeight;
    float getHeightFromFloat (float x, float y) const;
    float getHeightFromUint16 (float x, float y) const;
    float getHeightFromUint8 (float x, float y) const;
    float getHeightFromFlat (float x, float y) const;

public:
    GridMap ();
    ~GridMap ();
    bool loadData (char *filaname);
    void unloadData ();

    uint16 getArea (float x, float y);
    inline float getHeight (float x, float y)
    {
        return (this->*m_gridGetHeight)(x, y);
    }
    float getLiquidLevel (float x, float y);
    uint8 getTerrainType (float x, float y);
    ZLiquidStatus getLiquidStatus (float x, float y, float z, uint8 ReqLiquidType, LiquidData *data = 0);
};

struct CreatureMover
{
    CreatureMover () :
            x(0), y(0), z(0), ang(0)
    {
    }
    CreatureMover (float _x, float _y, float _z, float _ang) :
            x(_x), y(_y), z(_z), ang(_ang)
    {
    }

    float x, y, z, ang;
};

// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push, N), also any gcc version not support it at some platform
#if defined(__GNUC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif

struct InstanceTemplate
{
    uint32 map;
    uint32 parent;
    float startLocX;
    float startLocY;
    float startLocZ;
    float startLocO;
    uint32 script_id;
    bool allowMount;
};

enum LevelRequirementVsMode
{
    LEVELREQUIREMENT_HEROIC = 70
};

#if defined(__GNUC__)
#pragma pack()
#else
#pragma pack(pop)
#endif

typedef UNORDERED_MAP<Creature*, CreatureMover> CreatureMoveList;

#define MAX_HEIGHT            100000.0f                     // can be use for find ground height at surface
#define INVALID_HEIGHT       -100000.0f                     // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE
#define MAX_FALL_DISTANCE     250000.0f                     // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT
#define DEFAULT_HEIGHT_SEARCH     10.0f                     // default search distance to find height at nearby locations
#define MIN_UNLOAD_DELAY      1                             // immediate unload
typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType;

class Map: public GridRefManager<NGridType>
{
    friend class MapReference;
public:
    Map (uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode, Map* _parent = NULL);
    virtual ~Map ();

    MapEntry const* GetEntry () const
    {
        return i_mapEntry;
    }

    // currently unused for normal maps
    bool CanUnload (uint32 diff)
    {
        if (!m_unloadTimer)
            return false;
        if (m_unloadTimer <= diff)
            return true;
        m_unloadTimer -= diff;
        return false;
    }

    virtual bool Add (Player *);
    virtual void Remove (Player *, bool);
    template<class T> void Add (T *);
    template<class T> void Remove (T *, bool);

    void VisitNearbyCellsOf (WorldObject* obj, TypeContainerVisitor<Trinity::ObjectUpdater, GridTypeMapContainer> &gridVisitor, TypeContainerVisitor<Trinity::ObjectUpdater, WorldTypeMapContainer> &worldVisitor);
    virtual void Update (const uint32&);

    float GetVisibilityRange () const
    {
        return m_VisibleDistance;
    }
    //function for setting up visibility distance for maps on per-type/per-Id basis
    virtual void InitVisibilityDistance ();

    void PlayerRelocation (Player *, float x, float y, float z, float orientation);
    void CreatureRelocation (Creature *creature, float x, float y, float z, float ang, bool respawnRelocationOnFail = true);

    template<class T, class CONTAINER> void Visit (const Cell& cell, TypeContainerVisitor<T, CONTAINER> &visitor);

    bool IsGridLoaded (float x, float y) const
    {
        return IsGridLoaded(Trinity::ComputeGridCoord(x, y));
    }

    bool IsRemovalGrid (float x, float y) const
    {
        GridPair p = Trinity::ComputeGridPair(x, y);
        return !getNGrid(p.x_coord, p.y_coord) || getNGrid(p.x_coord, p.y_coord)->GetGridState() == GRID_STATE_REMOVAL;
    }

    bool IsLoaded (float x, float y) const
    {
        GridPair p = Trinity::ComputeGridPair(x, y);
        return loaded(p);
    }

    bool GetUnloadLock (const GridPair &p) const
    {
        return getNGrid(p.x_coord, p.y_coord)->getUnloadLock();
    }
    void SetUnloadLock (const GridPair &p, bool on)
    {
        getNGrid(p.x_coord, p.y_coord)->setUnloadExplicitLock(on);
    }
    void LoadGrid (float x, float y);
    bool UnloadGrid (const uint32 &x, const uint32 &y, bool pForce);
    virtual void UnloadAll ();

    void ResetGridExpiry (NGridType &grid, float factor = 1) const
    {
        grid.ResetTimeTracker(time_t(float(i_gridExpiry) * factor));
    }

    time_t GetGridExpiry (void) const
    {
        return i_gridExpiry;
    }
    uint32 GetId (void) const
    {
        return i_mapEntry->MapID;
    }

    static bool ExistMap (uint32 mapid, int gx, int gy);
    static bool ExistVMap (uint32 mapid, int gx, int gy);

    static void InitStateMachine ();
    static void DeleteStateMachine ();

    Map const * GetParent () const
    {
        return m_parentMap;
    }

    // some calls like isInWater should not use vmaps due to processor power
    // can return INVALID_HEIGHT if under z+2 z coord not found height
    float GetHeight (float x, float y, float z, bool pCheckVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;

    ZLiquidStatus getLiquidStatus (float x, float y, float z, uint8 ReqLiquidType, LiquidData *data = 0) const;

    uint16 GetAreaFlag (float x, float y, float z, bool *isOutdoors = 0) const;
    bool GetAreaInfo (float x, float y, float z, uint32 &mogpflags, int32 &adtId, int32 &rootId, int32 &groupId) const;

    bool IsOutdoors (float x, float y, float z) const;

    uint8 GetTerrainType (float x, float y) const;
    float GetWaterLevel (float x, float y) const;
    bool IsInWater (float x, float y, float z, LiquidData *data = 0) const;
    bool IsUnderWater (float x, float y, float z) const;

    static uint32 GetAreaIdByAreaFlag (uint16 areaflag, uint32 map_id);
    static uint32 GetZoneIdByAreaFlag (uint16 areaflag, uint32 map_id);
    static void GetZoneAndAreaIdByAreaFlag (uint32& zoneid, uint32& areaid, uint16 areaflag, uint32 map_id);

    uint32 GetAreaId (float x, float y, float z) const
    {
        return GetAreaIdByAreaFlag(GetAreaFlag(x, y, z), GetId());
    }

    uint32 GetZoneId (float x, float y, float z) const
    {
        return GetZoneIdByAreaFlag(GetAreaFlag(x, y, z), GetId());
    }

    void GetZoneAndAreaId (uint32& zoneid, uint32& areaid, float x, float y, float z) const
    {
        GetZoneAndAreaIdByAreaFlag(zoneid, areaid, GetAreaFlag(x, y, z), GetId());
    }

    void MoveAllCreaturesInMoveList ();
    void RemoveAllObjectsInRemoveList ();
    virtual void RemoveAllPlayers ();

    bool CreatureRespawnRelocation (Creature *c);          // used only in MoveAllCreaturesInMoveList and ObjectGridUnloader

    // assert print helper
    bool CheckGridIntegrity (Creature* c, bool moved) const;

    uint32 GetInstanceId () const
    {
        return i_InstanceId;
    }
    uint8 GetSpawnMode () const
    {
        return (i_spawnMode);
    }
    virtual bool CanEnter (Player* /*player*/)
    {
        return true;
    }
    const char* GetMapName () const;

    // have meaning only for instanced map (that have set real difficulty)
    Difficulty GetDifficulty () const
    {
        return Difficulty(GetSpawnMode());
    }
    bool IsRegularDifficulty () const
    {
        return GetDifficulty() == REGULAR_DIFFICULTY;
    }
    MapDifficulty const* GetMapDifficulty () const;

    bool Instanceable () const
    {
        return i_mapEntry && i_mapEntry->Instanceable();
    }
    bool IsDungeon () const
    {
        return i_mapEntry && i_mapEntry->IsDungeon();
    }
    bool IsNonRaidDungeon () const
    {
        return i_mapEntry && i_mapEntry->IsNonRaidDungeon();
    }
    bool IsRaid () const
    {
        return i_mapEntry && i_mapEntry->IsRaid();
    }
    bool IsRaidOrHeroicDungeon () const
    {
        return IsRaid() || i_spawnMode > DUNGEON_DIFFICULTY_NORMAL;
    }
    bool IsHeroic () const
    {
        return IsRaid() ? i_spawnMode >= RAID_DIFFICULTY_10MAN_HEROIC : i_spawnMode >= DUNGEON_DIFFICULTY_HEROIC;
    }
    bool Is25ManRaid () const
    {
        return IsRaid() && i_spawnMode & RAID_DIFFICULTY_MASK_25MAN;
    }          // since 25man difficulties are 1 and 3, we can check them like that
    bool IsBattleground () const
    {
        return i_mapEntry && i_mapEntry->IsBattleground();
    }
    bool IsBattleArena () const
    {
        return i_mapEntry && i_mapEntry->IsBattleArena();
    }
    bool IsBattlegroundOrArena () const
    {
        return i_mapEntry && i_mapEntry->IsBattlegroundOrArena();
    }

    bool GetEntrancePos (int32 &mapid, float &x, float &y)
    {
        if (!i_mapEntry)
            return false;
        return i_mapEntry->GetEntrancePos(mapid, x, y);
    }

    void AddObjectToRemoveList (WorldObject *obj);
    void AddObjectToSwitchList (WorldObject *obj, bool on);
    virtual void DelayedUpdate (const uint32 diff);

    void UpdateObjectVisibility (WorldObject* obj, Cell cell, CellPair cellpair);
    void UpdateObjectsVisibilityFor (Player* player, Cell cell, CellPair cellpair);

    void resetMarkedCells ()
    {
        marked_cells.reset();
    }
    bool isCellMarked (uint32 pCellId)
    {
        return marked_cells.test(pCellId);
    }
    void markCell (uint32 pCellId)
    {
        marked_cells.set(pCellId);
    }

    bool HavePlayers () const
    {
        return !m_mapRefManager.isEmpty();
    }
    uint32 GetPlayersCountExceptGMs () const;
    bool ActiveObjectsNearGrid (uint32 x, uint32 y) const;

    void AddWorldObject (WorldObject *obj)
    {
        i_worldObjects.insert(obj);
    }
    void RemoveWorldObject (WorldObject *obj)
    {
        i_worldObjects.erase(obj);
    }

    void SendToPlayers (WorldPacket const* data) const;

    typedef MapRefManager PlayerList;
    PlayerList const& GetPlayers () const
    {
        return m_mapRefManager;
    }

    //per-map script storage
    void ScriptsStart (std::map<uint32, std::multimap<uint32, ScriptInfo> > const& scripts, uint32 id, Object* source, Object* target);
    void ScriptCommandStart (ScriptInfo const& script, uint32 delay, Object* source, Object* target);

    // must called with AddToWorld
    template<class T>
    void AddToActive (T* obj)
    {
        AddToActiveHelper(obj);
    }

    void AddToActive (Creature* obj);

    // must called with RemoveFromWorld
    template<class T>
    void RemoveFromActive (T* obj)
    {
        RemoveFromActiveHelper(obj);
    }

    void RemoveFromActive (Creature* obj);

    template<class T> void SwitchGridContainers (T* obj, bool active);
    template<class NOTIFIER> void VisitAll (const float &x, const float &y, float radius, NOTIFIER &notifier);
    template<class NOTIFIER> void VisitFirstFound(const float &x, const float &y, float radius, NOTIFIER &notifier);
    template<class NOTIFIER> void VisitWorld (const float &x, const float &y, float radius, NOTIFIER &notifier);
    template<class NOTIFIER> void VisitGrid (const float &x, const float &y, float radius, NOTIFIER &notifier);
    CreatureGroupHolderType CreatureGroupHolder;

    void UpdateIteratorBack (Player *player);

    TempSummon *SummonCreature (uint32 entry, const Position &pos, SummonPropertiesEntry const *properties = NULL, uint32 duration = 0, Unit *summoner = NULL, uint32 vehId = 0, uint32 lowGUID = 0);
    Creature* GetCreature (uint64 guid);
    GameObject* GetGameObject (uint64 guid);
    DynamicObject* GetDynamicObject (uint64 guid);

    MapInstanced* ToMapInstanced ()
    {
        if (Instanceable())
            return reinterpret_cast<MapInstanced*>(this);
        else
            return NULL;
    }
    const MapInstanced* ToMapInstanced () const
    {
        if (Instanceable())
            return (const MapInstanced*) ((MapInstanced*) this);
        else
            return NULL;
    }

    InstanceMap* ToInstanceMap ()
    {
        if (IsDungeon())
            return reinterpret_cast<InstanceMap*>(this);
        else
            return NULL;
    }
    const InstanceMap* ToInstanceMap () const
    {
        if (IsDungeon())
            return (const InstanceMap*) ((InstanceMap*) this);
        else
            return NULL;
    }
private:
    void LoadMapAndVMap (int gx, int gy);
    void LoadVMap (int gx, int gy);
    void LoadMap (int gx, int gy, bool reload = false);
    GridMap *GetGrid (float x, float y);

    void SetTimer (uint32 t)
    {
        i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t;
    }

    void SendInitSelf (Player * player);

    void SendInitTransports (Player * player);
    void SendRemoveTransports (Player * player);

    bool CreatureCellRelocation (Creature *creature, Cell new_cell);

    void AddCreatureToMoveList (Creature *c, float x, float y, float z, float ang);
    CreatureMoveList i_creaturesToMove;

    bool loaded (const GridPair &) const;
    bool IsGridLoaded (const GridCoord &) const;
    void EnsureGridCreated (const GridPair &);
    bool EnsureGridLoaded (Cell const&);
    void EnsureGridLoadedAtEnter (Cell const&, Player* player = NULL);

    void buildNGridLinkage (NGridType* pNGridType)
    {
        pNGridType->link(this);
    }

    template<class T> void AddType (T *obj);
    template<class T> void RemoveType (T *obj, bool);

    NGridType* getNGrid (uint32 x, uint32 y) const
    {
        ASSERT(x < MAX_NUMBER_OF_GRIDS);
        ASSERT(y < MAX_NUMBER_OF_GRIDS);
        return i_grids[x][y];
    }

    bool isGridObjectDataLoaded (uint32 x, uint32 y) const
    {
        return getNGrid(x, y)->isGridObjectDataLoaded();
    }
    void setGridObjectDataLoaded (bool pLoaded, uint32 x, uint32 y)
    {
        getNGrid(x, y)->setGridObjectDataLoaded(pLoaded);
    }

    void setNGrid (NGridType* grid, uint32 x, uint32 y);
    void ScriptsProcess ();

    void UpdateActiveCells (const float &x, const float &y, const uint32 &t_diff);
protected:
    void SetUnloadReferenceLock (const GridPair &p, bool on)
    {
        getNGrid(p.x_coord, p.y_coord)->setUnloadReferenceLock(on);
    }

    ACE_Thread_Mutex Lock;

    MapEntry const* i_mapEntry;
    uint8 i_spawnMode;
    uint32 i_InstanceId;
    uint32 m_unloadTimer;
    float m_VisibleDistance;

    MapRefManager m_mapRefManager;
    MapRefManager::iterator m_mapRefIter;

    int32 m_VisibilityNotifyPeriod;

    typedef std::set<WorldObject*> ActiveNonPlayers;
    ActiveNonPlayers m_activeNonPlayers;
    ActiveNonPlayers::iterator m_activeNonPlayersIter;

private:
    Player* _GetScriptPlayerSourceOrTarget (Object* source, Object* target, const ScriptInfo* scriptInfo) const;
    Creature* _GetScriptCreatureSourceOrTarget (Object* source, Object* target, const ScriptInfo* scriptInfo, bool bReverse = false) const;
    Unit* _GetScriptUnit (Object* obj, bool isSource, const ScriptInfo* scriptInfo) const;
    Player* _GetScriptPlayer (Object* obj, bool isSource, const ScriptInfo* scriptInfo) const;
    Creature* _GetScriptCreature (Object* obj, bool isSource, const ScriptInfo* scriptInfo) const;
    WorldObject* _GetScriptWorldObject (Object* obj, bool isSource, const ScriptInfo* scriptInfo) const;
    void _ScriptProcessDoor (Object* source, Object* target, const ScriptInfo* scriptInfo) const;
    GameObject* _FindGameObject (WorldObject* pWorldObject, uint32 guid) const;

    time_t i_gridExpiry;

    //used for fast base_map (e.g. MapInstanced class object) search for
    //InstanceMaps and BattlegroundMaps...
    Map* m_parentMap;

    NGridType* i_grids[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS];
    GridMap *GridMaps[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS];
    std::bitset<TOTAL_NUMBER_OF_CELLS_PER_MAP * TOTAL_NUMBER_OF_CELLS_PER_MAP> marked_cells;

    //these functions used to process player/mob aggro reactions and
    //visibility calculations. Highly optimized for massive calculations
    void ProcessRelocationNotifies (const uint32 &diff);

    bool i_scriptLock;
    std::set<WorldObject *> i_objectsToRemove;
    std::map<WorldObject*, bool> i_objectsToSwitch;
    std::set<WorldObject*> i_worldObjects;
    std::multimap<time_t, ScriptAction> m_scriptSchedule;

    // Type specific code for add/remove to/from grid
    template<class T>
    void AddToGrid (T*, NGridType *, Cell const&);

    template<class T>
    void RemoveFromGrid (T*, NGridType *, Cell const&);

    template<class T>
    void DeleteFromWorld (T*);

    template<class T>
    void AddToActiveHelper (T* obj)
    {
        m_activeNonPlayers.insert(obj);
    }

    template<class T>
    void RemoveFromActiveHelper (T* obj)
    {
        // Map::Update for active object in proccess
        if (m_activeNonPlayersIter != m_activeNonPlayers.end())
        {
            ActiveNonPlayers::iterator itr = m_activeNonPlayers.find(obj);
            if (itr == m_activeNonPlayers.end())
                return;
            if (itr == m_activeNonPlayersIter)
                ++m_activeNonPlayersIter;
            m_activeNonPlayers.erase(itr);
        }
        else
            m_activeNonPlayers.erase(obj);
    }
};

enum InstanceResetMethod
{
    INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GLOBAL, INSTANCE_RESET_GROUP_DISBAND, INSTANCE_RESET_GROUP_JOIN, INSTANCE_RESET_RESPAWN_DELAY
};

class InstanceMap: public Map
{
public:
    InstanceMap (uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode, Map* _parent);
    ~InstanceMap ();
    bool Add (Player *);
    void Remove (Player *, bool);
    void Update (const uint32&);
    void CreateInstanceData (bool load);
    bool Reset (uint8 method);
    uint32 GetScriptId ()
    {
        return i_script_id;
    }
    InstanceScript* GetInstanceScript ()
    {
        return i_data;
    }
    void PermBindAllPlayers (Player *player);
    void UnloadAll ();
    bool CanEnter (Player* player);
    void SendResetWarnings (uint32 timeLeft) const;
    void SetResetSchedule (bool on, Player* player);

    uint32 GetMaxPlayers () const;
    uint32 GetMaxResetDelay () const;

    virtual void InitVisibilityDistance ();
private:
    bool m_resetAfterUnload;
    bool m_unloadWhenEmpty;
    InstanceScript* i_data;
    uint32 i_script_id;
};

class BattlegroundMap: public Map
{
public:
    BattlegroundMap (uint32 id, time_t, uint32 InstanceId, Map* _parent, uint8 spawnMode);
    ~BattlegroundMap ();

    bool Add (Player *);
    void Remove (Player *, bool);
    bool CanEnter (Player* player);
    void SetUnload ();
    //void UnloadAll(bool pForce);
    void RemoveAllPlayers ();

    virtual void InitVisibilityDistance ();
    Battleground* GetBG ()
    {
        return m_bg;
    }
    void SetBG (Battleground* bg)
    {
        m_bg = bg;
    }
private:
    Battleground* m_bg;
};

/*inline
 uint64
 Map::CalculateGridMask(const uint32 &y) const
 {
 uint64 mask = 1;
 mask <<= y;
 return mask;
 }
 */

template<class T, class CONTAINER>
inline void Map::Visit (const Cell& cell, TypeContainerVisitor<T, CONTAINER> &visitor)
{
    const uint32 x = cell.GridX();
    const uint32 y = cell.GridY();
    const uint32 cell_x = cell.CellX();
    const uint32 cell_y = cell.CellY();

    if (!cell.NoCreate() || loaded(GridPair(x, y)))
    {
        EnsureGridLoaded(cell);
        getNGrid(x, y)->Visit(cell_x, cell_y, visitor);
    }
}

template<class NOTIFIER>
inline void Map::VisitAll (const float &x, const float &y, float radius, NOTIFIER &notifier)
{
    CellPair p(Trinity::ComputeCellPair(x, y));
    Cell cell(p);
    cell.data.Part.reserved = ALL_DISTRICT;
    cell.SetNoCreate();

    TypeContainerVisitor<NOTIFIER, WorldTypeMapContainer> world_object_notifier(notifier);
    cell.Visit(p, world_object_notifier, *this, radius, x, y);
    TypeContainerVisitor<NOTIFIER, GridTypeMapContainer> grid_object_notifier(notifier);
    cell.Visit(p, grid_object_notifier, *this, radius, x, y);
}

// should be used with Searcher notifiers, tries to search world if nothing found in grid
template<class NOTIFIER>
inline void Map::VisitFirstFound(const float &x, const float &y, float radius, NOTIFIER &notifier)
{
    CellPair p(Trinity::ComputeCellPair(x, y));
    Cell cell(p);
    cell.SetNoCreate();

    TypeContainerVisitor<NOTIFIER, WorldTypeMapContainer> world_object_notifier(notifier);
    cell.Visit(p, world_object_notifier, *this, radius, x, y);
    if (!notifier.i_object)
    {
        TypeContainerVisitor<NOTIFIER, GridTypeMapContainer >  grid_object_notifier(notifier);
        cell.Visit(p, grid_object_notifier, *this, radius, x, y);
    }
}

template<class NOTIFIER>
inline void Map::VisitWorld (const float &x, const float &y, float radius, NOTIFIER &notifier)
{
    CellPair p(Trinity::ComputeCellPair(x, y));
    Cell cell(p);
    cell.data.Part.reserved = ALL_DISTRICT;
    cell.SetNoCreate();

    TypeContainerVisitor<NOTIFIER, WorldTypeMapContainer> world_object_notifier(notifier);
    cell.Visit(p, world_object_notifier, *this, radius, x, y);
}

template<class NOTIFIER>
inline void Map::VisitGrid (const float &x, const float &y, float radius, NOTIFIER &notifier)
{
    CellPair p(Trinity::ComputeCellPair(x, y));
    Cell cell(p);
    cell.data.Part.reserved = ALL_DISTRICT;
    cell.SetNoCreate();

    TypeContainerVisitor<NOTIFIER, GridTypeMapContainer> grid_object_notifier(notifier);
    cell.Visit(p, grid_object_notifier, *this, radius, x, y);
}
#endif
